import src.gameconf
import os
import sys

DEFAULT_STATE = { "quiet" : False}

def next_arg(args, default=None):
    '''
    Returns the first element of the list args.

    If args is empty, default is returned.  If args is not empty, the
    first element is removed from args and returned as the function
    value.
    '''
    if len(args) == 0:
        return default
    a = args[0]
    del args[0]
    return a

commands = {}
def register_command(name, func):
    commands[name] = func

def is_command(cmd):
    return cmd in commands

CURCONF = "curconf"
CURFILE = "curfile"
def load_game(args, state):
    '''
    load <filename>

        Loads the game file with name <filename>.'''
    game_file = next_arg(args)
    if game_file is not None:
        conf, _ = src.gameconf.load_gameconf_from_archive(game_file)
        #sys.stdout.write("Game file loaded: %s\n" % game_file)
        state[CURCONF] = conf
        state[CURFILE] = game_file
        state[game_file] = conf
        
register_command("load", load_game)


FILE1 = "file1"
FILE2 = "file2"

def compare_file1_file2(state):
    fn1 = state[FILE1]
    if state[CURFILE] == state[FILE1]:
        conf1 = state[CURCONF]
    else:
        conf1,_ = src.gameconf.load_gameconf_from_archive(fn1)
    if conf1 is None:
        sys.stderr.write("Error: could not read %s\n" % fn1)
        return

    fn2 = state[FILE2]
    conf2,_ = src.gameconf.load_gameconf_from_archive(fn2)
    if conf2 is None:
        sys.stderr.write("Error: could not read %s\n" % fn2)
        return
    sys.stdout.write("%d units in %s\n" % (len(conf1.units), fn1))
    sys.stdout.write("%d units in %s\n" % (len(conf2.units), fn2))
    uniq_units1 = [u for u in conf1.units if u not in conf2.units]
    uniq_units2 = [u for u in conf2.units if u not in conf1.units]
    changed_units = [ u for u in conf1.units 
                     if (u in conf2.units) and (conf1.units[u].full_cmp(conf2.units[u]) != 0)]
    if len(uniq_units1)> 0:
        sys.stdout.write("*** %d units only in %s:\n" % (len(uniq_units1), state[FILE1]))
        for u in uniq_units1:
            sys.stdout.write(conf1.units[u].get_save_str())
    if len(uniq_units2)> 0:
        sys.stdout.write("*** %d units only in %s:\n" % (len(uniq_units2), state[FILE2]))
        for u in uniq_units2:
            sys.stdout.write(conf2.units[u].get_save_str())
    if len(changed_units)> 0:
        sys.stdout.write("*** %d changed units:\n" % len(changed_units))
        for u in changed_units:
            sys.stdout.write("<<<\n")
            sys.stdout.write(conf1.units[u].get_save_str())
            sys.stdout.write("---\n")
            sys.stdout.write(conf2.units[u].get_save_str())
            sys.stdout.write(">>>\n")

def compare_games(args, state):
    '''
    compare <filename1> [<filename2>]
        Compares two games. If filename2 is present, the games in
        filename1 and filename2 are compared.
        If filename2 is not present, the current game, loaded with the 
        load-command, is compared to the game in filename1.'''
    filename1 = next_arg(args)
    if filename1 is None:
        sys.stderr.write("Two game files needed to compare games\n")
        return
    
    filename2 = next_arg(args)
    if (filename2 is None) and (CURCONF not in state):
        sys.stderr.write("Two game files needed to compare games.\n")
        return
    if filename2 is None:
        state[FILE1] = state[CURFILE]
        state[FILE2] = filename1
    else:
        state[FILE1] = filename1
        state[FILE2] = filename2
    compare_file1_file2(state)
register_command("compare", compare_games)


def list_games(args, state):
    '''
    list-games <dir>
        Lists the game files in directory <dir>.'''
    game_dir = next_arg(args, ".")

    files = os.listdir(game_dir)
    for f in files:
        dirname, basename = os.path.split(f)
        fname, ext = os.path.splitext(basename)
        if ext == ".zip":
            sys.stdout.write("%s\n" % basename)

register_command("list-games", list_games)

def list_areas(args, state):
    '''
    areas
        List areas.'''
    if state.has_key(CURCONF):
        ainfo = state[CURCONF].get_area_info()
        areas = ainfo.areas.keys()
        areas.sort()
        sys.stdout.write("\n".join(areas) + "\n")
    else:
        sys.stderr.write("No game loaded\n")
        
register_command("areas", list_areas)

def list_borders(args, state):
    '''
    borders <area-name>
        List borders of given area.'''
    area_name = next_arg(args, "")
    if area_name != "":
        if state.has_key(CURCONF):
            ainfo = state[CURCONF].get_area_info()
            if area_name in ainfo.areas:
                nbs = ainfo.get_neighbours(area_name)
                nbs.sort()
                sys.stdout.write("\n".join(nbs) + "\n")
            else:
                sys.stderr.write("No such area: %s\n" % area_name)
        else:
            sys.stderr.write("No game loaded.\n")
    

register_command("borders", list_borders)

def list_units(args, state):
    '''
    units
        List units.'''
    if state.has_key(CURCONF):
        unames = state[CURCONF].units.keys()
        unames.sort()
        sep = ""
        for un in unames:
            unit = state[CURCONF].get_unit(un)
            anames = [a.name for a in state[CURCONF].areas_for_unit(unit)]
            if len(anames)>1:
                anames.remove("The Game Board")
            if len(anames) == 0:
                sep += "**** "
            sys.stdout.write("%s%s: %s" % (sep, un, (", ".join(anames))))
            sep = "\n"
    else:
        sys.stderr.write("No game loaded\n")
        
register_command("units", list_units)

def script_game(args, state):
    '''
    script <filename>

        Loads and executes the scrip <filename>.
        
        A script is a plain text file. Each line can contain one 
        command (+ arguments).
        
        Empty lines and lines starting with # are ignored.'''
    script_file = next_arg(args)
    if script_file is not None:
        with open(script_file) as sf:
            cmd_lines =  [ l.strip().split() for l in sf
                          if not l.startswith('#') ]
            err_cmd_lines = [ l for l in cmd_lines
                             if (len(l)>0) and (not is_command(l[0]))]
            if len(err_cmd_lines)>0:
                sys.stderr.write("Error: the following lines do not start with a command:\n")
                sys.stderr.write("    " + ("\n    ".join(
                        [" ".join(line) for line in err_cmd_lines])) + "\n")
                sys.stderr.write("No commands have been executed.")
            else:
                for line in cmd_lines:
                    if len(line)>0:
                        process_args(line, state)
                        
        
register_command("script", script_game)

def usage():
    cmds = commands.keys()
    cmds.sort()
    desc = []
    for c in cmds:
        desc.append(commands[c].__doc__)
    cmd_descriptions = "\n    ".join(desc)
    text = '''
This is a command-line utility to inspect and manipulate game files.

Usage:

    %s [ ( cmd [arg]* ) | -q ]*
    
where cmd is a command from the list below and arg is list of
arguments to the command. There can be more than one command.

Options:

  -q : select quiet mode: do not produce unwanted output.
  
Commands:
%s
''' % (sys.argv[0], cmd_descriptions)
    return text

def write_usage(args, state):
    '''
    help
        Writes a usage-message.'''
    sys.stdout.write("%s\n" % usage())
register_command("help", write_usage)
    
def no_such_command(cmd, args, state):
    sys.stderr.write("Error: No such command: %s\n" % cmd)
    sys.stderr.flush()
    write_usage(args, state)

def process_args(args, state):
    cmd = next_arg(args)
    while cmd == "-q":
        state["quiet"] = True
        cmd = next_arg(args)
    cmd_func = commands.get(cmd, None)
    if cmd_func is None:
        no_such_command(cmd, args, state)
        while (len(args)>0) and (not is_command(args[0])):
            del args[0]
        return
    if not state["quiet"]:
        sys.stdout.write("## Executing %s\n" % cmd)
    cmdargs = []
    while len(args)>0 and args[0] not in commands:
        cmdargs.append(args[0])
        del args[0]
    cmd_func(cmdargs, state)


if __name__ == "__main__":
    if len(sys.argv) > 1:
        
        args = sys.argv[1:]
        state = DEFAULT_STATE
        while len(args)>0:
            process_args(args, state)
